#include "path_selector.hh"
#include "path_manager.hh"
#include "grid.hh"
#include <random>

namespace kratos {
namespace grid {

PathSelector::PathSelector(PathManager * manager)
    : manager_(manager) {
}

PathSelector::~PathSelector() {
}

const SitePtrVector & PathSelector::select(uint16_t siteType, uint32_t serviceType, std::uint64_t serviceID) {
    sites_.clear();
    return _select(siteType, serviceType, serviceID);
}

RoundRobinSelector::RoundRobinSelector(PathManager * manager)
    : PathSelector(manager), current_(0) {
}

RoundRobinSelector::~RoundRobinSelector() {
}

Property::PathFindType RoundRobinSelector::getType() {
    return Property::PathFindType::RoundRobin;
}

const SitePtrVector & RoundRobinSelector::_select(uint16_t siteType, uint32_t serviceType, std::uint64_t serviceID) {
    manager_->getGrid()._getSites(siteType, serviceType, sites_, serviceID);
    if (sites_.empty()) {
        return sites_;
    }
    if (current_ >= sites_.size()) {
        current_ = 0;
    }
    auto site = sites_[current_++];
    sites_.clear();
    sites_.push_back(site);
    return sites_;
}

RandomSelector::RandomSelector(PathManager * manager)
    : PathSelector(manager) {
}

RandomSelector::~RandomSelector() {
}

Property::PathFindType RandomSelector::getType() {
    return Property::PathFindType::Random;
}

const SitePtrVector & RandomSelector::_select(uint16_t siteType, uint32_t serviceType, std::uint64_t serviceID) {
    manager_->getGrid()._getSites(siteType, serviceType, sites_, serviceID);
    if (sites_.empty()) {
        return sites_;
    }
    std::random_device rd;
    std::default_random_engine engine(rd());
    std::uniform_int_distribution<> dis(0, sites_.size() - 1);
    auto dice = std::bind(dis, engine);
    auto site = sites_[dice()];
    sites_.clear();
    sites_.push_back(site);
    return sites_;
}

BroadcastSelector::BroadcastSelector(PathManager * manager)
    : PathSelector(manager) {
}

BroadcastSelector::~BroadcastSelector() {
}

Property::PathFindType BroadcastSelector::getType() {
    return Property::PathFindType::Broadcast;
}

const SitePtrVector & BroadcastSelector::_select(uint16_t siteType, uint32_t serviceType, std::uint64_t serviceID) {
    manager_->getGrid()._getSites(siteType, serviceType, sites_, serviceID);
    return sites_;
}

}
}
